;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; PCI demo for DJGPP or 32-bit Watcom C, using 32-bit PCI BIOS
;
; Chris Giese     <geezer@execpc.com>     http://my.execpc.com/~geezer
; Release date: Feb 23, 2005
; This code is public domain (no copyright).
; You can do whatever you want with it.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;SEGMENT _TEXT PUBLIC CLASS=CODE USE32
SECTION .text
BITS 32

PCI_INSTALL_CHECK	equ 0B101h

PCI_READ_CONFIG_BYTE	equ 0B108h
PCI_READ_CONFIG_WORD	equ 0B109h
PCI_READ_CONFIG_DWORD	equ 0B10Ah

PCI_WRITE_CONFIG_BYTE	equ 0B10Bh
PCI_WRITE_CONFIG_WORD	equ 0B10Ch
PCI_WRITE_CONFIG_DWORD	equ 0B10Dh

%macro	EXP	1
	GLOBAL $_%1	; leading underscores
	$_%1:
%endmacro

%macro	IMP	1
	EXTERN $_%1
	%define %1 _%1
%endmacro

; IMPORTS
; from PCIBIO32.C:
IMP g_last_pci_bus

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:			pci_detect_help
; action:		uses BIOS32 to detect PCIBIOS and sets bus count
; in:			arguments on stack per C prototype
; out (error):		return value != 0
; out (success):	return value == 0
; modifies:		EAX
; minimum CPU:		'386
; notes:		C prototype:
;			int pci_detect_help(unsigned long entry,
;				unsigned long virt_to_phys);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

EXP pci_detect_help
	push ebp
		mov ebp,esp
		push edx
		push ecx
		push ebx
			mov [_pci_entry + 4],cs

; set _pci_entry = BIOS32 entry point
			mov eax,[ebp + 8]
			add eax,[ebp + 12]
			mov [_pci_entry + 0],eax
			mov eax,0x49435024 ; "$PCI"
			mov bl,0
			call far [_pci_entry]

			or al,al
			je ok1
			mov eax,-1
			jmp short err
ok1:
; set _pci_entry = PCIBIOS entry point. EBX=base address of PCI code...
			add ebx,edx		; ...add entry point...
			add ebx,[ebp + 12];	; ...and virt_to_phys...
			mov [_pci_entry + 0],ebx ; ...and store it

			mov ax,PCI_INSTALL_CHECK
			call far [_pci_entry]

			or ah,ah
			je ok2
			mov eax,-1
			jmp short err
ok2:
			movzx ecx,cl
			mov [g_last_pci_bus],ecx
;;
;; printf("version %u.%u detected, ", (ebx >> 8) & 0xFF, ebx & 0xFF);
;;
			xor eax,eax
err:
		pop ebx
		pop ecx
		pop edx
	pop ebp
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:			pci_read_config_byte
; action:		uses 32-bit PCI BIOS to read PCI config byte
; in:			arguments on stack per C prototype
; out (error):		return value != 0
; out (success):	return value == 0
; modifies:		EAX, *val
; minimum CPU:		'386
; notes:		C prototype:
;			int pci_read_config_byte(pci_t *pci,
;				unsigned reg, unsigned char *val);
; typedef struct
; {
;	unsigned char bus, dev, fn;
; } pci_t;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

EXP pci_read_config_byte
	push ebp
	mov ebp,esp
		push edi
		push ecx
		push ebx
			mov ax,PCI_READ_CONFIG_BYTE
			mov edi,[ebp + 8]
			xor ebx,ebx
			mov bh,[edi + 0] ; bus
			mov bl,[edi + 1] ; dev
			shl bl,3
			or  bl,[edi + 2] ; fn
			mov edi,[ebp + 12] ; reg
			call far [_pci_entry]
			mov ebx,[ebp + 16] ; val
			mov [ebx],cl	; *val
		pop ebx
		pop ecx
		pop edi

		mov al,ah
		movzx eax,al
	pop ebp
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:			pci_read_config_word
; action:		uses 32-bit PCI BIOS to read PCI config word
; in:			arguments on stack per C prototype
; out (error):		return value != 0
; out (success):	return value == 0
; modifies:		EAX, *val
; minimum CPU:		'386
; notes:		C prototype:
;			int pci_read_config_word(pci_t *pci,
;				unsigned reg, unsigned short *val);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

EXP pci_read_config_word
	push ebp
	mov ebp,esp
		push edi
		push ecx
		push ebx
			mov ax,PCI_READ_CONFIG_WORD
			mov edi,[ebp + 8]
			xor ebx,ebx
			mov bh,[edi + 0] ; bus
			mov bl,[edi + 1] ; dev
			shl bl,3
			or  bl,[edi + 2] ; fn
			mov edi,[ebp + 12] ; reg
			call far [_pci_entry]
			mov ebx,[ebp + 16] ; val
			mov [ebx],cx	; *val
		pop ebx
		pop ecx
		pop edi

		mov al,ah
		movzx eax,al
	pop ebp
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:			pci_read_config_dword
; action:		uses 32-bit PCI BIOS to read PCI config dword
; in:			arguments on stack per C prototype
; out (error):		return value != 0
; out (success):	return value == 0
; modifies:		EAX, *val
; minimum CPU:		'386
; notes:		C prototype:
;			int pci_read_config_dword(pci_t *pci,
;				unsigned reg, unsigned long *val);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

EXP pci_read_config_dword
	push ebp
	mov ebp,esp
		push edi
		push ecx
		push ebx
			mov ax,PCI_READ_CONFIG_DWORD
			mov edi,[ebp + 8]
			xor ebx,ebx
			mov bh,[edi + 0] ; bus
			mov bl,[edi + 1] ; dev
			shl bl,3
			or  bl,[edi + 2] ; fn
			mov edi,[ebp + 12] ; reg
			call far [_pci_entry]
			mov ebx,[ebp + 16] ; val
			mov [ebx],ecx	; *val
		pop ebx
		pop ecx
		pop edi

		mov al,ah
		movzx eax,al
	pop ebp
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Note: this is in the code segment...
_pci_entry:
	dd 0 ; offset (EIP)
	dw 0 ; selector/segment (CS)
